﻿using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using Yoozoo.Gameplay.RTS;

namespace Yoozoo.Mars.Got.SpriteAnim
{
    //[BurstCompile]
    //public struct UpdateParticleJob : IJobParticleSystem
    public struct UpdateTroopsJob : IJobParallelForBatch
    {
        public float deltaTime;
        public int specialCountAtlasOffset;
        
        // 该动作是不是十六方向，索引为动作代号
        [ReadOnly] public NativeArray<bool> isDirect16;
        // 动画分成 动作 和 方向 两个部分
        // 动作的编号见：SPRITE_ACTION_ENUM，目前只用到0~5这6个动作
        // 方向的编号见：DIRCTION_INDEX_ENUM, 但实际只有5方向，左右会翻转处理
        // 由于 ParticleSystemJobData 使用 aliveTimePercent, 所以这里也用这种方式记录动画的起始和结束时间
        // 动画信息的索引 = 动作编号 * 方向数 + 方向编号

        [ReadOnly] public NativeArray<float4> spriteAnimTextureRect;
        [ReadOnly] public NativeArray<float2> spriteAnimFrameSize;
        [ReadOnly] public NativeArray<float2> spriteAnimFrameOffset;
        [ReadOnly] public NativeArray<int> spriteAnimFrameCount;

        [ReadOnly] public NativeArray<float> changeColorOffsetX;
        [ReadOnly] public NativeArray<float> changeColorOffsetY;
        [ReadOnly] public NativeArray<float> changeColorOffsetX2;
        [ReadOnly] public NativeArray<float> changeColorOffsetY2;
        [ReadOnly] public NativeArray<float> startOffsetX;
        [ReadOnly] public NativeArray<float> startOffsetY;
        [ReadOnly] public NativeArray<float> rectWidth;
        [ReadOnly] public NativeArray<float> rectHeight;
        
        public NativeArray<Matrix4x4> matrices;
        public NativeArray<int> spriteCurrentFrame;
        public NativeArray<float> spriteTimeLeft;
        public AnimSpriteCreatorData data;

        const float FRAMES_PER_SECOND = 30;
        const int MAX_ANIM_FRAMES = 100;
        const int MAX_SPRITE_DIRECTION = 9;

        [ReadOnly] public float cameraRotation;

        public void Execute(int startIndex, int count)
        {
            const int PositionRow = 0;
            const int SizePivotRow = 1;
            const int TexRectRow = 2;
            const int TweakRow = 3;
            int particleCount = startIndex + count;
            for (int i = startIndex; i < particleCount; i++)
            {
                if (data.removes[i])
                {
                    matrices[i] = Matrix4x4.zero;
                    data.removes[i] = false;
                    continue;
                }

                var mtx = matrices[i];
                // 根据rotation计算动画方向，判断是否需要翻转，设置size
                float3 rotation = data.rotations[i];
                float spriteSize = data.spriteSizes[i];
                int action = data.actions[i];
                bool isd16 = isDirect16[action];
                int direction = ParticleJobHelper.GetDirection(rotation, isd16,cameraRotation);
                bool flip = ParticleJobHelper.GetFlip(direction, isd16);
                var size = new float2(flip ? -spriteSize : spriteSize, spriteSize);

                // 设置位置 透明度
                float3 position = data.positions[i];
                float3 offset = data.offsets[i];
                var p = position + offset;

                byte alpha = data.alphas[i];
                mtx.SetRow(PositionRow, new Vector4(p.x, p.y, p.z, alpha / 255f));

                // 根据动作编号和方向确定要播放的动画
                int animationIndex = ParticleJobHelper.GetAnimationIndex(direction, isd16);
                int index = action * MAX_SPRITE_DIRECTION + animationIndex;
                // 读取动画的开始时间和结束时间
                int frameCount = spriteAnimFrameCount[index];
                // 根据当前时间计算下一帧
                int currentFrame = spriteCurrentFrame[i];
                float timeLeft = spriteTimeLeft[i];
                float speed = data.speeds[i];
                bool restart = data.restarts[i];

                var changed = false;
                if (restart)
                {
                    currentFrame = 0;
                    timeLeft = 0;
                    changed = true;
                }
                else if (deltaTime > 0 && speed > 0)
                {
                    timeLeft += deltaTime * speed;
                    int frameOffset = Mathf.FloorToInt(timeLeft * FRAMES_PER_SECOND);
                    currentFrame += frameOffset;
                    timeLeft -= frameOffset / FRAMES_PER_SECOND;
                    changed = true;
                    if (currentFrame >= frameCount)
                    {
                        if (frameCount != 0)
                        {
                            currentFrame %= frameCount;
                        }
                        else
                        {
                            currentFrame = 0;
                        }
                    }
                }

                if (changed)
                {
                    var offsetIndex = index * MAX_ANIM_FRAMES + currentFrame;
                    mtx.SetRow(TexRectRow, spriteAnimTextureRect[offsetIndex]);

                    var frameSize = spriteAnimFrameSize[offsetIndex];
                    size = size * frameSize;
                    var pivot = spriteAnimFrameOffset[offsetIndex];
                    mtx.SetRow(SizePivotRow, new Vector4(size.x, size.y, pivot.x, pivot.y));

                    float offsetX = 0;
                    float offsetY = 0;
                    var team = data.team[i];
                    if (specialCountAtlasOffset == 0)
                    {
                        if (team == 0 || team == 1)
                        {
                            offsetX = changeColorOffsetX[offsetIndex];
                            offsetY = changeColorOffsetY[offsetIndex];
                        }
                        else if (team == 3)
                        {
                            offsetX = changeColorOffsetX2[offsetIndex];
                            offsetY = changeColorOffsetY2[offsetIndex];
                        }
                    }
                    else
                    {
                        if (team == 0)
                        {
                            
                        }
                        else if (team == 1)
                        {
                            offsetX = changeColorOffsetX[offsetIndex];
                            offsetY = changeColorOffsetY[offsetIndex];
                        }
                        else if (team == 2)
                        {
                            offsetX = changeColorOffsetX2[offsetIndex];
                            offsetY = changeColorOffsetY2[offsetIndex];
                        }
                        else if (team == 3)
                        {
                            offsetX = changeColorOffsetX[offsetIndex + specialCountAtlasOffset];
                            offsetY = changeColorOffsetY[offsetIndex + specialCountAtlasOffset];
                        }
                        else if (team == 4)
                        {
                            offsetX = changeColorOffsetX2[offsetIndex + specialCountAtlasOffset];
                            offsetY = changeColorOffsetY2[offsetIndex + specialCountAtlasOffset];
                        }
                    }
                    
                    var startX = startOffsetX[offsetIndex];
                    var startY = startOffsetY[offsetIndex];
                    var rectWidth = this.rectWidth[offsetIndex];
                    var rectHeight = this.rectHeight[offsetIndex];

                    // evil reinterpret trick
                    var temp = spriteTimeLeft;
                    temp.ReinterpretStore(i, math.f32tof16(startX) + (math.f32tof16(startY) << 16));
                    var start = temp.ReinterpretLoad<float>(i);
                    temp.ReinterpretStore(i, math.f32tof16(rectWidth) + (math.f32tof16(rectHeight) << 16));
                    var rect = temp.ReinterpretLoad<float>(i);

                    mtx.SetRow(TweakRow, new Vector4(start, rect, offsetX, offsetY));

                    spriteCurrentFrame[i] = currentFrame;
                    spriteTimeLeft[i] = timeLeft;
                }

                matrices[i] = mtx;

                if (restart)
                    data.restarts[i] = false;
            }
        }
    }
}
